package com.legendframework.core.dao.yaml;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.legendframework.core.dao.AbstractDao;
import com.legendframework.core.dao.annotation.Entity;
import com.legendframework.core.dao.exception.DaoException;
import com.legendframework.core.dao.support.ClassUtils;
import com.legendframework.core.dao.transaction.YamlTransaction;
import com.legendframework.core.dao.transaction.enums.TransactionStatus;
import com.legendframework.core.dao.transaction.pojo.IOTransaction;
import com.legendframework.core.dao.utils.DaoStringUtils;
import com.legendframework.core.dao.wrapper.Wrapper;
import com.legendframework.core.dao.yaml.enums.YamlStorageType;
import com.legendframework.core.dao.yaml.support.BaseYml;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public abstract class AbstractYamlDao<T> extends AbstractDao<T> implements Cache<T>{

    /**
     * 数据缓存池
     * 实现存取同步
     */
    private Map<Serializable,T> cache;

    private final YamlTransaction yamlTransaction;

    private Gson gson = new GsonBuilder().serializeNulls().create();

    /**
     * 线程数据缓存池
     * 实现存取同步
     */
    private final ThreadLocal<Map<Serializable,T>> threadLocal = new ThreadLocal<>();

    private final static Map<Class,CacheTransaction> caches = new ConcurrentHashMap<>();

    public AbstractYamlDao(Class<T> entityType, YamlTransaction yamlTransaction) {
        super(entityType);
        if (entityType != null) {
            cache = new ConcurrentHashMap<>();
            caches.put(entityType,this);
        }
        this.yamlTransaction = yamlTransaction;
    }

    public static Map<Class, CacheTransaction> getCaches() {
        return caches;
    }

    /**
     * 获取缓存
     * @return
     */
    private Map<Serializable,T> getCache(){
        if (!isTransactionRun()) {
            //当前没有事务
            return cache;
        }
        //读锁
        transactionLock();
        //当前有事务，取副本
        Map<Serializable, T> serializableTMap = threadLocal.get();
        if (serializableTMap == null) {
            //拷贝副本
            Map<Serializable,T> resutl = new ConcurrentHashMap<>();
            for (Map.Entry<Serializable, T> entry : cache.entrySet()) {
                if (entry.getValue() != null) {
                    resutl.put(entry.getKey(),cloneBean(entry.getValue()));
                }
            }
            threadLocal.set(resutl);
            return resutl;
        }
        return serializableTMap;
    }

    /**
     * 提交缓存到主分支
     */
    @Override
    public void onCacheCommit() {
        Map<Serializable, T> serializableTMap = threadLocal.get();
        if (serializableTMap != null) {
            cache = serializableTMap;
            threadLocal.remove();
        }
    }

    /**
     * 删除缓存，不提交到主分支
     */
    @Override
    public void onCacheBack() {
        threadLocal.remove();
    }

    /**
     * 修改实体类
     *
     * 如果字段不为空则替换，为空则不替换
     *
     * @param source 需要替换的实体类
     * @param target 目标实体类
     * @return
     */
    protected void updateEntity(T source, T target){
        Class<?> cls = source.getClass();
        List<Field> fields = ClassUtils.getByClass(cls);
        for (Field field : fields) {
            try {
                Object fieldResult = ClassUtils.getGetterMethodByField(cls, field).invoke(source);
                if (fieldResult == null) {
                    continue;
                }
                //把该字段的值替换到目标实体类中
                ClassUtils.getSetterMethodByField(cls,field).invoke(target,fieldResult);
            } catch (Throwable e) {
                throw new DaoException("获取实体类字段数据时失败",e);
            }
        }
    }
    /**
     * 获取实体类上的YamlFile注解信息
     * @param path 绝对路径前缀
     * @param typeClass 实体类
     * @return
     */
    protected String getYamlFileName(String path , Class<T> typeClass){
        String fileName = null;
        if (typeClass.isAnnotationPresent(Entity.class)) {
            Entity annotation = typeClass.getAnnotation(Entity.class);
            if (DaoStringUtils.isNotEmpty(annotation.yamlFileName())) {
                fileName = annotation.yamlFileName();
            }
        }
        if (fileName == null) {
            throw new DaoException("实体类必须实现@Entity注解中的yamlFileName字段");
        }
        //格式化fileName
        YamlStorageType yamlStorageType = typeClass.getAnnotation(Entity.class).yamlStorageType();
        if (yamlStorageType == YamlStorageType.ONE_TO_MANY || yamlStorageType == YamlStorageType.ONE_TO_ONE) {
            if (!fileName.endsWith(".yml")) {
                fileName += ".yml";
            }
        }
        if (path.endsWith("/")) {
            if (fileName.startsWith("/")) {
                return path+fileName.substring(1);
            }else {
                return path+fileName;
            }
        }else if (fileName.startsWith("/")){
            return path+fileName;
        }else {
            return path+"/"+fileName;
        }
    }


    /**
     * 获取所有数据
     * @return
     */
    abstract List<T> getAll();

    /**
     * 对条件构造器的解析
     *
     * @param wrapper 条件
     * @return
     */
    protected List<T> filter(Wrapper<T> wrapper){
        if (wrapper == null) {
            return getAll();
        }
        return getAll().stream().filter(wrapper::check).collect(Collectors.toList());
    }

    /**
     * 获取所有缓存数据
     * @return
     */
    @Override
    public Collection<T> getCacheAll() {
        return getCache().values().stream().map(this::cloneBean).collect(Collectors.toList());
    }

    /**
     * 克隆Bean对象
     * @param t
     * @return
     */
    protected T cloneBean(T t){
        return gson.fromJson(gson.toJson(t),entityType);
    }

    /**
     * 获取一条缓存数据
     * @param key key
     * @return
     */
    @Override
    public T getCacheOne(Serializable key) {
        return cloneBean(getCache().get(key));
    }

    /**
     * 根据Key删除一条数据
     * @param key key
     */
    @Override
    public void deleteCacheByKey(Serializable key) {
        getCache().remove(key);
    }

    /**
     * 删除多个Key的数据
     * @param keys keyes
     */
    @Override
    public void deleteCacheBatchKey(Serializable[] keys) {
        if (keys == null) {
            return;
        }
        for (Serializable key : keys) {
            deleteCacheByKey(key);
        }
    }

    /**
     * 放置一条数据
     * @param key key
     * @param t 数据
     */
    @Override
    public void putCacheOne(Serializable key, T t) {
        if (t != null) {
            getCache().put(key,t);
        }
    }

    /**
     * put多个数据
     * @param map map
     */
    @Override
    public void putCacheBatch(Map<Serializable, T> map) {
        getCache().putAll(map);
    }

    @Override
    public void reload() {
        clear();
    }

    @Override
    public void flush() {
        getCache().values().forEach(this::insert);
    }

    /**
     * 判断当前事务是否正在运行
     * @return
     */
    private boolean isTransactionRun(){
        //获取当前事务
        IOTransaction<BaseYml<Object>> peek = yamlTransaction == null ? null : yamlTransaction.getThreadLocalData().peek();
        return peek != null && peek.getStatus() == TransactionStatus.BIGIN;
    }

    /**
     * 获取锁，如果其他事务未释放锁，则一直等待
     */
    private void transactionLock() {
        IOTransaction<BaseYml<Object>> ioTransaction = yamlTransaction == null ? null : yamlTransaction.getThreadLocalData().peek();
        if (ioTransaction != null) {
            try {
                while (!yamlTransaction.getLock().tryLock(ioTransaction.getId(), 10, TimeUnit.MILLISECONDS)) {
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void clear() {
        getCache().clear();
    }
}
